﻿
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.IO;

namespace lumo.display.object2D
{
    /// <summary>
    /// TileMap class.
    /// </summary>
    public class TileLayer : IObject2D, IOriginable2D, ICroppable, IColorable
    {
        /// <summary>
        /// Tile struct.
        /// </summary>
        public struct Tile
        {
            /// <summary> The source rectangle..</summary>
            public Rectangle SourceRectangle;
            /// <summary> The texture.</summary>
            public Texture2D Texture;
        }

        /// <summary>Parent game class..</summary>
        public lumo.LumoComponent Parent = null;
        /// <summary>The Width.</summary>
        public uint TileWidth = 0;
        /// <summary>The Height</summary>
        public uint TileHeight = 0;
        /// <summary>Width of the layer.</summary>
        public uint Width = 0;
        /// <summary>Height of the layer..</summary>
        public uint Height = 0;
        /// <summary> The array.</summary>
        public Tile[,] TileArray = null;
        /// <summary> The array.</summary>
        private string _TilesetFolder = null;

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="Game">Parent game class.</param>
        /// <param name="Layer">The layer</param>
        /// <param name="TilesetFolder">The tileset folder.</param>
        public TileLayer(lumo.LumoComponent Game, Tiled.TileLayer Layer, string TilesetFolder = "Graphics/Tilesets")
        {
            Parent = Game;
            this.TilesetFolder = TilesetFolder;
            
            TileWidth = Layer.Parent.TileWidth;
            TileHeight = Layer.Parent.TileHeight;
            Width = Layer.Width;
            Height = Layer.Height;

            TileArray = new Tile[Width, Height];

            UInt32 i = 0;
            for (uint y = 0; y < Height; ++y)
            {
                for (uint x = 0; x < Width; ++x)
                {
                    SetTile(ref TileArray[x, y], Layer.Data[i++], Layer);
                }
            }

        }

        /// <summary>
        /// The tileset folder.
        /// </summary>
        public string TilesetFolder 
        {
            get
            {
                return _TilesetFolder;
            }
            set
            {
                if (value == null || value.Length <= 0)
                    _TilesetFolder = ".";
                else
                    _TilesetFolder = value;
            }
        }

        /// <summary>
        /// Set the tile.
        /// </summary>
        /// <param name="Tile">The tile</param>
        /// <param name="GID">Global ID for the tile.</param>
        /// <param name="Layer">The layer class</param>
        void SetTile(ref Tile Tile, UInt32 GID, Tiled.TileLayer Layer)
        {
            foreach (Tiled.Tileset tileset in Layer.Parent.Tilesets)
            {
               if (tileset.GetRectangle(GID, ref Tile.SourceRectangle))
               {
                   Tile.Texture = Parent.Content.Load<Texture2D>(Path.Combine(_TilesetFolder, Path.GetFileNameWithoutExtension(tileset.Image)));
                   return;
               }
            }
        }

    #region Interface Implementation

        #region IObject2D

        /// <summary>
        /// Draw implementaion of IObject2D
        /// </summary>
        /// <param name="spriteBatch">The spritebatch object</param>
        public void Draw(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch)
        {
            if (!_Visible)
                return;
            int SrcX = 0;
            int SrcY = 0;
            int SrcWidth  = spriteBatch.GraphicsDevice.Viewport.Width;
            int SrcHeight = spriteBatch.GraphicsDevice.Viewport.Height;
            if (_SourceRectangle.HasValue)
            {
                SrcX = SourceRectangle.Value.X;
                SrcY = SourceRectangle.Value.Y;
                SrcWidth = SourceRectangle.Value.Width;
                SrcHeight = SourceRectangle.Value.Height;
            }

            int OriginX = (int)_Origin.X;
            int OriginY = (int)_Origin.Y;
            int SX = ((int)(OriginX / TileWidth)) - 1;
            int SY = ((int)(OriginY / TileHeight)) - 1;
            int W = ((int)(SrcWidth / TileWidth)) + 2;
            if (SrcWidth % TileWidth > 0)
                ++W;
            int H = ((int)(SrcHeight / TileHeight)) + 2;
            if (SrcHeight % TileHeight > 0)
                ++H;
            int EX = W + SX;
            int EY = H + SY;

            if (SX < 0)
                SX = 0;
            if (SY < 0)
                SY = 0;
            if (EX >= Width)
                EX = (int)Width - 1;
            if (EY >= Height)
                EY = (int)Height- 1;

            Vector2 Position = new Vector2(0, SrcY + SY * TileHeight - OriginY);
            for (int cur_y = SY; cur_y <= EY; ++cur_y)
            {
                Position.X = SrcX - OriginX + SX * TileWidth;
                for (int cur_x = SX; cur_x <= EX; ++cur_x)
                {
                    if (TileArray[cur_x, cur_y].Texture != null)
                        spriteBatch.Draw(TileArray[cur_x, cur_y].Texture, Position, TileArray[cur_x, cur_y].SourceRectangle, _Color);
                    Position.X += TileWidth;
                }
                Position.Y += TileHeight;
            }
        }

        /// <summary>Private draw order float.</summary>
        private float _DrawOrder = 0f;
        /// <summary>
        /// Simple implementation of draw order.
        /// </summary>
        public float DrawOrder { get { return _DrawOrder; } set { _DrawOrder = value; } }

        /// <summary>Private visible variable.</summary>
        private bool _Visible = true;        /// <summary>
        /// Simple implementation of Visible.
        /// </summary>
        public bool Visible { get { return _Visible; } set { _Visible = value; } }

        #endregion

        #region IOriginable2D

        /// <summary>
        /// Size of obejct.
        /// </summary>
        public Vector2 Size { get { return this.OriginalSize; } }
        /// <summary> 
        /// Pivot point 
        /// </summary>
        private Vector2 _Origin = Vector2.Zero;
        /// <summary>
        /// Implemetation of Origin. IOriginable2D.
        /// </summary>
        public Vector2 Origin { get { return _Origin; } set { _Origin = value; } }
        /// <summary>
        /// Implementation of OX. IOriginable2D.
        /// </summary>
        public float OX { get { return _Origin.X; } set { _Origin.X = value; } }
        /// <summary>
        /// Implementation of OY. IOriginable2D.
        /// </summary>
        public float OY { get { return _Origin.Y; } set { _Origin.Y = value; } }

        #endregion

        #region ICroppable

        /// <summary> 
        /// Crop area (null for no crop) 
        /// </summary>
        private Rectangle? _SourceRectangle = null;
        /// <summary>
        /// Implementation of OriginalSize. ISourceRectangleable.
        /// </summary>
        public Vector2 OriginalSize
        {
            get
            {
                return new Vector2(Width * TileWidth, Height * TileHeight);
            }
        }

        /// <summary>
        /// Implementation of SourceRectangle. ISourceRectangleable.
        /// </summary>
        public Rectangle? SourceRectangle { get { return _SourceRectangle; } set { _SourceRectangle = value; } }

        #endregion

        #region IColorable

        /// <summary> 
        /// Colormask 
        /// </summary>
        private Color _Color = Color.White;
        /// <summary>
        /// Implemetaion of Color. IColorable
        /// </summary>
        public Color Color { get { return _Color; } set { _Color = value; } }
        /// <summary>
        /// Implemetaion of ColorR. IColorable
        /// </summary>
        public byte ColorR { get { return _Color.R; } set { _Color.R = value; } }
        /// <summary>
        /// Implemetaion of ColorG. IColorable
        /// </summary>
        public byte ColorG { get { return _Color.G; } set { _Color.G = value; } }
        /// <summary>
        /// Implemetaion of ColorB. IColorable
        /// </summary>
        public byte ColorB { get { return _Color.B; } set { _Color.B = value; } }
        /// <summary>
        /// Implemetaion of ColorA. IColorable
        /// </summary>
        public byte ColorA { get { return _Color.A; } set { _Color.A = value; } }

        #endregion

    #endregion
    }
}
